<?php
namespace Monkey\Database;

use MonkeyException;
use Monkey;

/**
 * 数据库模型\Monkey\Database\Model
 * @package    Monkey\Database
 * @author     HuangYi
 * @copyright  Copyright (c) 2011-07-01——2013-03-30
 * @license    New BSD License
 * @version    $Id: \Monkey\Database\Model.php 版本号 2013-03-30 $
 */
class Model{
    protected $_table;
    protected $_focus;
    protected $_pri_name='';  //表主键名
    protected $_pri_is_auto=false; //表主键是否自增
    protected $_fields; //表所有字段
    protected $_fields_default=array();//字段默认值
    protected $_fields_type=array();//字段类型
    protected $_errorMassage;
    /**
     * 缓存对象
     * @var \Monkey\Cache\_Interface
     */
    protected $_cacher;
    /**
     * 数据操作对象
     * @var \Monkey\DatabaseManager
     */
    protected $_dbManager;
    /**
     * 构造方法
     * @param string $table 表名
     * @param \Monkey\DatabaseManager $databaseManager 数据层管理器
     */
    public function  __construct($table, Monkey\DatabaseManager &$databaseManager){
        $this->_table=$table;
        $this->_cacher = Monkey\CacheManager::getCacheByType();
        $this->_dbManager = $databaseManager;
        $this->_loadTable();
        $this->_fields=array_keys($this->_fields_default);
    }
    /**
     * 获取数据层管理器
     * @return Monkey\DatabaseManager
     */
    public function &getDatabaseManager(){
        return $this->_dbManager;
    }
    /**
     * 通过主键从数据库读取一行数据，并填充到焦点数据中
     * @param string $id 主键值，默认时读取第一行
     * @return array|boolean
     */
    public function selectFocus($id=null){
        $select=$this->_dbManager->select();
        if($id){
            $select->where($this->_pri_name.'=<id>',$id);
        }  else {
            $select->limit(1,0);
        }
        $result=$select->table($this->_table)->exec();
        if($result){
            return $this->_focus=$result->result();
        }
        return FALSE;
    }
    /**
     * 将焦点数据插入到数据库中
     * @return boolean
     */
    public function insertFocus(){
        $this->_pri_is_auto && $this->unsetFocusField($this->_pri_name);
        $insert=$this->_dbManager->insert()->table($this->_table)->values($this->dataFill($this->_focus))->exec();
        if($insert->affectedRows()>0){
            $this->setPRI($insert->insertId());
            return TRUE;
        }
        $this->_errorMassage=$insert->errorMessage();
        return FALSE;
    }
    /**
     * 删除焦点数据行
     * @return boolean
     */
    public function deleteFocus(){
        if(!isset($this->_focus[$this->_pri_name])){
            $this->_errorMassage= '采用焦点数据删除数据库时，主键值不存在，无法定位删除行';
            return FALSE;
        }
        $delete=$this->_dbManager->delete()->table($this->_table)->where($this->_pri_name.'='.$this->_focus[$this->_pri_name])->exec();
        if($delete->isSuccess()){
            $this->_focus=array();
            return TRUE;
        }
        return FALSE;
    }
    /**
     * 更新数据到数据库中
     * @return boolean
     */
    public function updateFocus(){
        if(!isset($this->_focus[$this->_pri_name])){
            $this->_errorMassage= '采用焦点数据更新数据库时，主键值不存在，无法定位更新行';
            return FALSE;
        }
        $PRI=$this->_focus[$this->_pri_name];
        unset($this->_focus[$this->_pri_name]);
        $update=$this->_dbManager->update()->table($this->_table)->values($this->dataFilter($this->_focus))
            ->where($this->_pri_name.'='.$PRI)->exec();
        if($update->isSuccess()){
            return true;
        }else{
            $this->_errorMassage=$update->errorMessage();
            return false;
        }
    }
    /**
     * 设置焦点数据
     * @param array $data 新的数据（入库时自动去除sql非法字符，开发人员无需关心）
     * @param array $fieldFix 字段读取时的修正映射
     * @return $this
     */
    public function setFocus(array $data =  array(),array $fieldFix=null){
        $this->_focus = $this->dataFilter($data, $fieldFix);
        return $this;
    }
    /**
     * 获取焦点数据
     * @return array
     */
    public function getFocus(){
        return $this->_focus;
    }
    /**
     * 合并到焦点数据
     * @param array $data 新的数据（入库时自动去除sql非法字符，开发人员无需关心）
     * @param array $fieldFix 字段读取时的修正映射
     * @return $this
     */
    public function mergeToFocus(array $data =  array(),array $fieldFix=null){
        $this->_focus = array_merge($this->_focus, $this->dataFilter($data, $fieldFix));
        return $this;
    }
    /**
     * 设置焦点数据的主键值
     * @param int $PRIvalue 主键值（入库时自动去除sql非法字符，开发人员无需关心）
     * @return $this
     */
    public function setPRI($PRIvalue){
        $this->_focus[$this->_pri_name]=(int)$PRIvalue;
        return $this;
    }
    /**
     * 获取主键信息
     * @param string $type 信息类别
     *                  val（默认） 当前焦点的主键值
     *                  name 主键名
     *                  auto 是否为自增字段
     *
     * @return mixed
     */
    public function getPRI($type='val'){
        if($type=='name'){
            return $this->_pri_name;
        }elseif($type=='auto'){
            return $this->_pri_is_auto;
        }else{
            return $this->_focus[$this->_pri_name];
        }
    }
    /**
     * 设置焦点数据的字段值
     * @param string $field 字段名。焦点中不存在这个字段时，自动忽略，使得本次调用无效果
     * @param string $value 字段值。（入库时自动去除sql非法字符，开发人员无需关心）
     * @return $this
     */
    public function setField($field,$value){
        if(array_key_exists($field,$this->_fields_default)) $this->_focus[$field]=$value;
        return $this;
    }
    /**
     * 获取焦点数据的字段值
     * @param string $field 字段名
     * @return string|integer
     */
    public function getField($field){
        return $this->_focus[$field];
    }
    /**
     * 获取字段信息
     * @param string $type 信息类别
     *                  default（默认） 字段默认值
     *                  name 字段名
     *                  type 字段类型
     * @return array
     */
    public function getFieldInfo($type='default'){
        if($type=='name'){
            return $this->_fields;
        }elseif($type=='type'){
            return $this->_fields_type;
        }else{
            return $this->_fields_default;
        }
    }
    /**
     * 获取错误信息
     * @return string
     */
    public function errorMessage(){
        return $this->_errorMassage;
    }
    //////////////////////////////////////////////////
    //以下是辅助方法：
    private static function _error($message) {
        MonkeyException::halt('模型出错:' . $message);
    }
    /**
     * 删除焦点数据的某字段
     * @param string $field
     * @return $this
     */
    private function unsetFocusField($field){
        if(array_key_exists($field,$this->_focus)) unset($this->_focus[$field]);
        return $this;
    }
    /**
     * 格式化指定数据，未指定的字段由默认值填充，即使指定了字段，还会对字段按照数据库中的字段顺序排序（入库时自动去除sql非法字符，开发人员无需关心）
     * @param array $data 指定的行数据，$row=array(field_i=>value_i,field_k=>value_k,...)
     * @param array $fieldFix 修正字段名
     * @return array
     */
    protected function dataFill(array $data=array(),array $fieldFix=null){
        $fieldFix && $this->_fieldFix($data, $fieldFix);
        $defaultData=$this->_fields_default;
        foreach ($data as $fieldname => $value) {
            array_key_exists($fieldname,$defaultData) and $defaultData[$fieldname]=$value;
        }
        return $defaultData;
    }
    /**
     * 格式化指定数据，未指定的字段不设置，还会对字段按照数据库中的字段顺序排序（入库时自动去除sql非法字符，开发人员无需关心）
     * @param array $data 指定的行数据，$row=array(field_i=>value_i,field_k=>value_k,...)
     * @param array $fieldFix 修正字段名
     * @return array
     */
    protected function dataFilter(array $data=array(),array $fieldFix=null){
        $fieldFix && $this->_fieldFix($data, $fieldFix);
        $defaultData=$this->_fields_default;
        $t=array();
        if(empty($data)) return $t;
        foreach ($defaultData as $fieldname => $value) {
            array_key_exists($fieldname,$data) and $t[$fieldname]=$data[$fieldname];
        }
        return $t;
    }
    /**
     * 修正字段名
     * @param array $data
     * @param array $fieldFix
     */
    private function _fieldFix(array &$data,array $fieldFix=null){
        foreach ($fieldFix as $source => $field) {
            if(array_key_exists($source,$data)){
                $data[$field]=$data[$source];
                unset($data[$source]);
            }
        }
    }
    private function _loadTable(){
        $cache_key='tableMateFor_'.$this->_table;$tableMate=null;
        if(!$this->_cacher->fetch($cache_key,$tableMate)){
            $tableMate=$this->_dbManager->queryHelper()->getTableMate($this->_table);
            $this->_cacher->store($cache_key,$tableMate);
        }
        !$tableMate && MonkeyException::halt('表【'.$this->_table. '】的字段信息读取失败!');
        $this->_fields_default=$tableMate['fields_default'];
        $this->_fields_type=$tableMate['fields_type'];
        $this->_pri_name=$tableMate['pri_name'];
        $this->_pri_is_auto=$tableMate['pri_is_auto'];
    }
}